package com.bims.jreport.dao;

import com.bims.jreport.entity.TPageEntity;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.jooq.*;
import org.jooq.impl.TableImpl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

import static com.bims.jreport.dao.JooqDaoUtil.*;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.jooq.impl.DSL.row;

@Slf4j
public class JooqDao<T extends TableImpl<R>, R extends UpdatableRecord<R>> {
    @Getter
    @Setter
    private DSLContext dsl;

    private T table;



    public JooqDao(T table) {
        this.table = table;
    }

    /**
     * 获取表的主键数组
     *
     * @param table
     * @return
     */
    static Field<?>[] getTablePKs(TableImpl table) {
        UniqueKey<?> key = table.getPrimaryKey();
        return key == null ? null : key.getFieldsArray();
    }

    /**
     * 如果字段不可为空，但字段当前值为空，设置该字段未改变
     *
     * @param record
     */
    static final void resetNotNullFieldChangeState(Record record) {
        int size = record.size();

        for (int i = 0; i < size; i++) {
            if (record.get(i) == null && !record.field(i).getDataType().nullable()) {
                record.changed(i, false);
            }
        }
    }

    private R getNewRecord(R rcd, boolean updatePks, Field<?>[] pk) {
        R record = dsl.newRecord(table, rcd);
        if (!updatePks && pk != null) {
            for (Field<?> field : pk) {
                record.changed(field, false);
            }
        }
        resetNotNullFieldChangeState(record);
        return record;
    }

    private List<R> getNewRecords(Collection<R> rcds, boolean updatePks) {
        List<R> result = new ArrayList<R>();
        Field<?>[] pk = getTablePKs(table);

        for (R rcd : rcds) {
            result.add(getNewRecord(rcd, updatePks, pk));
        }

        return result;
    }


    /**
     * 根据id获取记录
     * @param id
     * @param <L>
     * @return
     */
    public <L> R getById(L id) {
        Field<?>[] pk = getTablePKs(table);
        R record = null;

        if (pk != null) {
            record = dsl
                    .selectFrom(table)
                    .where(equal(pk, id))
                    .fetchOne();
        }

        return record;
    }

    public <L> R findById(L id){
        return getById(id);
    }

    /**
     * 获取所有记录
     * @return
     */
    public List<R> getAll() {
        return dsl.selectFrom(table).fetch();
    }

    public List<R> findAll(){
        return getAll();
    }


    /**
     * 根据查询状态进行count
     * @param conditions
     * @return
     */
    public int count(Collection<Condition> conditions) {
        val step = dsl.selectCount().from(table);
        if (conditions != null) {
            step.where(conditions);
        }
        return step.fetchOne(0, Integer.class);
    }

    public boolean fetchExists(Condition condition){
        return dsl.fetchExists(table, condition);
    }

    public int update(R rcd) {
        if (rcd == null) {
            return 0;
        }
        return update(singletonList(rcd))[0];
    }

    public int[] update(R ...rcds) {
        return update(asList(rcds));
    }

    public int[] update(Collection<R> rcds) {
        if (rcds.size() > 1) {
            return dsl.batchUpdate(getNewRecords(rcds, false)).execute();
        }

        if (rcds.size() == 1) {
            int rs = getNewRecords(rcds, false).get(0).update();
            return new int[]{rs};
        }
        return EMPTY_INT;
    }

    /**
     * 更新目标记录的主键信息
     * @param source
     * @param target
     */
    private void updateTargetRecordPKData(R source, R target) {
        for (Field f : getTablePKs(table)) {
            target.set(f, source.get(f));
        }
    }

    /**
     * 清除主键数据
     * @param rcd
     */
    public void clearPKData(R rcd) {
        val pk = getTablePKs(table);
        if (pk != null) {
            for (Field<?> field : pk) {
                rcd.set(field, null);
            }
        }
    }

    /**
     * 插入数据
     * @param record
     * @return
     */
    public int insert(R record) {
        if (record == null) {
            return 0;
        }
        return insert(singletonList(record))[0];
    }

    public int[] insert(R... rcds) {
        return insert(asList(rcds));
    }

    public int[] insert(Collection<R> rcds) {

//        rcds.forEach(rcd -> {
//            clearPKData(rcd);
//        });

        if (rcds.size() > 1) {

            List<R> recordList = getNewRecords(rcds, true);
            int[] rss = dsl.batchInsert(recordList).execute();
//            int i = 0;
//            for (R o : rcds) {
//                if (rss[i] > 0) {
//                    updateTargetRecordPKData(recordList.get(i), o);
//                }
//                i++;
//            }
            return rss;
        }
        if (rcds.size() == 1) {
            R obj = rcds.iterator().next();
            R rcd = getNewRecord(obj, true, getTablePKs(table));
            int rs = rcd.insert();
//            if (rs > 0) {
//                updateTargetRecordPKData(rcd, obj);
//            }
            return new int[]{rs};
        }
        return EMPTY_INT;
    }

    /**
     * 直接根据rcd记录插入到db中
     * @param rcd
     * @return
     */
    public int insertDirectly(R rcd){
        if (rcd == null) {
            return 0;
        }
        R nRcd = getNewRecord(rcd, true, null);
        int rs = nRcd.insert();
        return rs;
    }


    /**
     * 删除数据
     * @param rcd
     * @return
     */
    public int delete(R rcd) {
        if (rcd == null) {
            return 0;
        }
        return delete(singletonList(rcd))[0];
    }

    public int[] delete(R ...rcds) {
        return delete(asList(rcds));
    }


    public int[] delete(Collection<R> rcds) {

        if (rcds.size() > 1) {
            return dsl.batchDelete(getNewRecords(rcds, false)).execute();
        }

        if (rcds.size() == 1) {
            int rs = getNewRecords(rcds, false).get(0).delete();
            return new int[]{rs};
        }
        return EMPTY_INT;
    }


    /**
     * 根据id删除数据
     * @param ids
     * @return
     */
    public <L> int deleteById(L... ids) {
        return deleteById(asList(ids));
    }

    public <L> int deleteById(Collection<L> ids){

        Field<?>[] pk = getTablePKs(table);
        if (pk != null) {
            return dsl.delete(table).where(equal(pk, ids)).execute();
        }
        return 0;
    }

//    public int deleteById(List<Object> ids) {
//        Field<?>[] pk = getTablePKs(table);
//        if (pk != null) {
//            return dsl.delete(table).where(equal(pk, ids)).execute();
//        }
//        return 0;
//    }

    private <L> Condition equal(Field<?>[] pk, L id) {
        if (pk.length == 1) {
            return ((Field<Object>) pk[0]).equal(pk[0].getDataType().convert(id));
        } else {
            return row(pk).equal((Record) id);
        }
    }

    private <L> Condition equal(Field<?>[] pk, Collection<L> ids) {
        if (pk.length == 1) {
            if (ids.size() == 1) {
                return equal(pk, ids.iterator().next());
            }
            return pk[0].in(pk[0].getDataType().convert(ids));
        }
        return row(pk).in(ids.toArray(EMPTY_RECORD));
    }


    /**
     * 根据字段和字段值获取数据
     * @param field
     * @param values
     * @param <Z>
     * @return
     */
    public <Z> List<R> fetch(Field<Z> field, Z... values) {
        return dsl
                .selectFrom(table)
                .where(field.in(values))
                .fetch();
    }

    /**
     * 根据状态和排序,查询数据
     * @param conditions
     * @param orderCollections
     * @return
     */
    public List<R> fetch(Collection<Condition> conditions, Collection<? extends SortField<?>> orderCollections){
        if (conditions == null) {
            conditions = EMPTY_CONDITION_COLLECTION;
        }
        if (orderCollections == null) {
            orderCollections = EMPTY_SORT_COLLECTION;
        }
        return dsl.selectFrom(table).where(conditions).orderBy(orderCollections).fetch();
    }


    public <Z> R fetchOne(Field<Z> field, Z value) {
        R record = dsl
                .selectFrom(table)
                .where(field.equal(value))
                .fetchOne();

        return record;
    }

    public <Z> Optional<R> fetchOptional(Field<Z> field, Z value) {
        return Optional.ofNullable(fetchOne(field, value));
    }

    /**
     * 分页查询信息
     *
     * @param page
     * @param conditions
     */
    public void pageQuery(TPageEntity<R> page, Collection<Condition> conditions) {
        pageQuery(page, conditions, null);
    }

    /**
     * 分页查询信息
     *
     * @param page
     * @param conditions
     * @param orderCollections
     */
    public void pageQuery(TPageEntity<R> page, Collection<Condition> conditions, Collection<? extends SortField<?>> orderCollections) {
        if (conditions == null) {
            conditions = EMPTY_CONDITION_COLLECTION;
        }
        if (orderCollections == null) {
            orderCollections = EMPTY_SORT_COLLECTION;
        }
        int count = count(conditions);
        page.setEntityCount(count);
        int start = (page.getPageNo() - 1) * page.getPageSize();
        if (start < count) {
            List<R> list = dsl.selectFrom(table).where(conditions).orderBy(orderCollections).limit(start, page.getPageSize()).fetch();
            page.setEntities(list);
        }
    }

}
